package com.thitech.sif.utils;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Socket;

import com.thitech.sif.exception.OutputException;
import com.thitech.sif.exception.ServiceExecuteException;
import com.thitech.sif.exception.SocketLinkException;
import com.thitech.sif.utils.log.LogFactory;
import com.thitech.sif.utils.log.SifLogger;


/**
 * socket工具类
 * 
 * @author shisheng.lian
 */
public class SocketUtil {
	private SifLogger logger = LogFactory.getLogger(getClass());

	private Socket socketClient;
	private InputStream in = null;
	private OutputStream out = null;
	private final int DEFAULT_BODY_LENGTH = 1024;

	/**
	 * 初始化Socket工具类
	 */
	public SocketUtil(String ip, int port, int connectTimeOut, int readTimeOut)
			throws SocketLinkException {
		logger.debug("创建Socket实例->IP:" + ip + "" + port + ": CTO:"
				+ connectTimeOut + "; RTO:" + readTimeOut);
		socketClient = new Socket();

		try {
			socketClient.connect(new InetSocketAddress(ip, port),
					connectTimeOut);
			socketClient.setSoTimeout(readTimeOut);
		} catch (Exception e) {
			throw new SocketLinkException("链接Socket服务器失败", e);
		}
	}

	/**
	 * 无参构造函数
	 * 
	 */
	public SocketUtil() {
	}

	/**
	 * 发送Socket报文
	 */
	public InputStream send(byte[] bytes) throws ServiceExecuteException {
		try {
			out = socketClient.getOutputStream();
			out.write(bytes);
			out.flush();

			try {
				socketClient.shutdownOutput();
			} catch (IOException e) {
				logger.error("socketClient.shutdownOutput()异常", e);
			}
			in = socketClient.getInputStream();
		} catch (Exception e) {
			throw new ServiceExecuteException("Socket服务执行失败", e);
		}
		return in;
	}

	/**
	 * 发送 Socket报文
	 */
	public InputStream send(String doc) throws ServiceExecuteException {
		return send(doc, "UTF-8");
	}

	/**
	 * 发送Socket报文
	 * 
	 * @param doc
	 * @param character
	 * @return
	 * @throws ServiceExecuteException
	 */
	public InputStream send(String doc, String character)
			throws ServiceExecuteException {
		InputStream in = null;
		try {
			in = send(doc.getBytes(character));
		} catch (UnsupportedEncodingException e) {
			logger.error("TheCharacter Encoding is not supported.", e);
		}
		return in;
	}

	/**
	 * 得到Socket返回报文
	 * 
	 * @param ins
	 * @param character
	 * @return
	 * @throws OutputException
	 */
	public String getResponse(InputStream ins, String character)
			throws OutputException {
		StringBuffer resultXML = new StringBuffer();
		BufferedInputStream bis = null;
		try {
			bis = new BufferedInputStream(ins);
			byte[] buffer = new byte[1024];
			int n = bis.read(buffer);
			String stmp = new String(buffer, 0, n, character);
			resultXML.append(stmp);
		} catch (Exception e) {
			throw new OutputException("处理Socket返回报文失败", e);
		} finally {
			if (null != bis) {
				try {
					bis.close();
				} catch (Exception e) {
					logger.error("关闭Socket流对象失败", e);
				}
			}
		}
		return resultXML.toString();
	}

	/**
	 * 获取Socket返回报问 （默认编码为UTF-8）
	 * 
	 * @param ins
	 * @return
	 * @throws OutputException
	 */
	public String getResponse(InputStream ins) throws OutputException {
		return getResponse(ins, "UTF-8");
	}

	/**
	 * 获取Socket返回报文，本方法根据报文头中定义的长度进行报文获取
	 * 
	 * @param ins
	 * @param character
	 * @param headLength
	 * @return
	 * @throws OutputException
	 */
	public String getResponse(InputStream ins, String character, int headLength)
			throws OutputException {
		StringBuffer resultXML = new StringBuffer();
		BufferedInputStream bis = null;
		try {
			bis = new BufferedInputStream(ins);

			byte[] headBuff = new byte[headLength];
			logger.debug("Get Head Start");
			int readHeadLen = bis.read(headBuff);
			logger.debug("Get Head End readHeadLen" + readHeadLen);
			if (readHeadLen != headLength) {
				logger.debug("总报文长度不足，报文头长：" + headLength + ",实际报文长度为："
						+ readHeadLen);
			} else {
				logger.debug("Get Response start");
				String headContent = new String(headBuff, character);
				logger.debug("Get Response start headContent" + headContent);
				int bodyLen = Integer.parseInt(headContent);
				logger.debug("Get Response start bodyLen " + bodyLen);
				resultXML.append(headContent);

				if (bodyLen > 0) {
					int j = 0;
					int readCount = 0;
					byte[] bodyByte = new byte[bodyLen];
					while (true) {
						byte[] bodyBuff = new byte[DEFAULT_BODY_LENGTH];
						int readSize = bis.read(bodyBuff);
						trimToSize(bodyBuff, readSize);
						readCount = readCount + readSize;
						String tempBodyContent = new String(bodyBuff, character);
						logger.info("Get Socket Response body: " + j
								+ "---readSize" + readSize + "----内容"
								+ tempBodyContent);
						j++;
						if (readCount == bodyLen) {
							break;
						}
					}
					String bodyContent = new String(bodyByte, character);
					resultXML.append(bodyContent);
				} else {
					logger.error("" + bodyLen);
				}
				logger.debug("Get Response End:" + resultXML.toString());
			}
		} catch (Exception e) {
			throw new OutputException("处理Socket返回报文失败", e);
		} finally {
			if (null != bis) {
				try {
					bis.close();
				} catch (Exception e) {
					logger.error("关闭Socket流对象失败", e);
				}
			}
		}
		return resultXML.toString();
	}

	/**
	 * 获取Socket返回报文，本方法根据报文头中定义的长度进行报文获取（默认编码为UTF-8）
	 */
	public String getResponse(InputStream ins, int headLength)
			throws OutputException {
		return getResponse(ins, "UTF-8", headLength);
	}

	/**
	 * 关闭socket服务
	 */
	public void close() {
		if (null != out) {
			try {
				out.close();
			} catch (IOException e) {
				logger.error("关闭Socket OutputStream 失败", e);
			}
		}
		if (null != in) {
			try {
				in.close();
			} catch (IOException e) {
				logger.error("关闭Socket InputStream失败", e);
			}
		}
		if (null != socketClient && socketClient.isConnected()
				&& !socketClient.isClosed()) {
			try {
				socketClient.close();
			} catch (IOException e) {
				logger.error("关闭Socket失败", e);
			}
		}
	}

	private byte[] trimToSize(byte[] b, int realSize) {
		byte[] result = new byte[realSize];
		System.arraycopy(b, 0, result, 0, realSize);
		return result;
	}
}